/*
    作者：zx
    时间：2022.11.9
    描述：IDT表的设置与异常处理程序的实现
*/

#include "cpu/irq.h" 
#include "cpu/cpu.h"
#include "comm/cpu_instr.h"
#include "os_cfg.h"
#include "tools/log.h"
#include "core/task.h"

#define IDT_TABLE_SIZE 128


static gate_desc_t idt_table[IDT_TABLE_SIZE];
void irq_init(void)
{
    // idt表中所有异常处理程序全设置为未定义
	for (uint32_t i = 0; i < IDT_TABLE_SIZE; i++) {
    	gate_desc_set(idt_table + i, KERNEL_SELECTOR_CS, (uint32_t)exception_handler_unknown,
                  GATE_P_PRESENT | GATE_DPL0 | GATE_TYPE_IDT);
	}

    // 安装实际异常处理程序
    irq_install(IRQ0_DE,(uint32_t)exception_handler_divider);
    irq_install(IRQ1_DB,(uint32_t)exception_handler_debug);
    irq_install(IRQ2_NMI,(uint32_t)exception_handler_NMI);
    irq_install(IRQ3_BP,(uint32_t)exception_handler_breakpoint);
    irq_install(IRQ4_OF,(uint32_t)exception_handler_overflow);
    irq_install(IRQ5_BR,(uint32_t)exception_handler_bound_range);
    irq_install(IRQ6_UD,(uint32_t)exception_handler_invalid_opcode);
    irq_install(IRQ7_NM,(uint32_t)exception_handler_device_not_available);
    irq_install(IRQ8_DF,(uint32_t)exception_handler_double_fault);

    irq_install(IRQ10_TS,(uint32_t)exception_handler_invalid_tss);
    irq_install(IRQ11_NP,(uint32_t)exception_handler_segement_not_present);
    irq_install(IRQ12_SS,(uint32_t)exception_handler_stack_segment_fault);
    irq_install(IRQ13_GP,(uint32_t)exception_handler_general_protection);
    irq_install(IRQ14_PF,(uint32_t)exception_handler_page_fault);

    irq_install(IRQ16_MF,(uint32_t)exception_handler_fpu_error);
    irq_install(IRQ17_AC,(uint32_t)exception_handler_alignment_check);
    irq_install(IRQ18_MC,(uint32_t)exception_handler_machine_check);
    irq_install(IRQ19_XM,(uint32_t)exception_handler_simd_exception);
    irq_install(IRQ20_VE,(uint32_t)exception_handler_virtual_exception);
    irq_install(IRQ21_CP,(uint32_t)exception_handler_control_exception);


    lidt((uint32_t)idt_table, sizeof(idt_table));

    // 启动两块8259A中断芯片、中断控制器初始化
    pic_init();
}

// 打印异常结构体信息
void exception_frame_info(exception_frame_t * exception_frame)
{
    uint32_t ss,esp;
    if(exception_frame->cs & 0x3)
    {
        ss = exception_frame->ss3;
        esp = exception_frame->esp3;
    }
    else
    {
        ss = exception_frame->ds;
        esp = exception_frame->esp;
    }
    log_printf("IRQ: %d, ERROR CODE: %d", exception_frame->idx, exception_frame->error_code);
    log_printf("CS: %d\nDS: %d\nDS: %d\nSS: %d\nFS: %d\nGS: %d\n",
        exception_frame->cs, exception_frame->ds, exception_frame->es, 
        exception_frame->ds, ss, exception_frame->gs);
    log_printf(
        "EAX: %x\n"
        "EBX: %x\n"
        "ECX: %x\n"
        "EDX: %x\n"
        "EDI: %x\n"
        "ESI: %x\n"
        "EBP: %x\n"
        "ESP: %x\n",
        exception_frame->eax,exception_frame->ebx,exception_frame->ecx,exception_frame->edx,
        exception_frame->edi,exception_frame->esi,exception_frame->ebp,esp
    );
    log_printf("EIP: %x\nEFLAGS: %x\n",exception_frame->eip,exception_frame->eflags);
}

void do_default_handler(exception_frame_t * exception_frame, const char * message)
{
    log_printf("----- EXCEPTION HAPPEND -----");
    log_printf("IRQ ERROR: %s", message);
    exception_frame_info(exception_frame);
       // 判断触发异常的程序段权限级,区分系统和用户进程
    if(exception_frame->cs & 0x3)
    {
        sys_exit(exception_frame->error_code);
    }
    else{
        while(1)
        {
            hlt();
        }
    }
}

void do_handler_unknown(exception_frame_t * exception_frame)
{   // irq_idx -1
    do_default_handler(exception_frame,"unknown exception");
}

void do_handler_divider(exception_frame_t * exception_frame)
{   // irq_idx 0
    do_default_handler(exception_frame,"divder exception");
}

void do_handler_debug(exception_frame_t * exception_frame)
{   // irq_idx 1
    do_default_handler(exception_frame,"debug exception");
}
void do_handler_NMI(exception_frame_t * exception_frame)
{   // irq_idx 2
    do_default_handler(exception_frame,"NMI exception");
}
void do_handler_breakpoint(exception_frame_t * exception_frame)
{   // irq_idx 3
    do_default_handler(exception_frame,"breakpoint exception");
}
void do_handler_overflow(exception_frame_t * exception_frame)
{   // irq_idx 4
    do_default_handler(exception_frame,"overflow exception");
}
void do_handler_bound_range(exception_frame_t * exception_frame)
{   // irq_idx 5
    do_default_handler(exception_frame,"bound_range exception");
}
void do_handler_invalid_opcode(exception_frame_t * exception_frame)
{   // irq_idx 6
    do_default_handler(exception_frame,"invalid_opcode exception");
}
void do_handler_device_not_available(exception_frame_t * exception_frame)
{   // irq_idx 7
    do_default_handler(exception_frame,"device_not_available exception");
}
void do_handler_double_fault(exception_frame_t * exception_frame)
{   // irq_idx 8
    do_default_handler(exception_frame,"device_not_available exception");
}

void do_handler_invalid_tss(exception_frame_t * exception_frame)
{   // irq_idx 10
    do_default_handler(exception_frame,"invalid_tss exception");
}
void do_handler_segement_not_present(exception_frame_t * exception_frame)
{   // irq_idx 11
    do_default_handler(exception_frame,"segement_not_present exception");
}
void do_handler_stack_segment_fault(exception_frame_t * exception_frame)
{   // irq_idx 12
    do_default_handler(exception_frame,"stack_segment_fault exception");
}
void do_handler_general_protection(exception_frame_t * exception_frame)
{   // irq_idx 13
    log_printf("--------------------------------");
    log_printf("IRQ/Exception happend: General Protection.");
    if (exception_frame->error_code & ERROR_EXT) {
        log_printf("the exception occurred during delivery of an "
                "event external to the program, such as an interrupt"
                "or an earlier exception.");
    } else {
        log_printf("the exception occurred during delivery of a"
                    "software interrupt (INT n, INT3, or INTO).");
    }
    
    if (exception_frame->error_code & ERROR_IDT) {
        log_printf("the index portion of the error code refers "
                    "to a gate descriptor in the IDT");
    } else {  
        log_printf("the index refers to a descriptor in the GDT");
    }
    
    log_printf("segment index: %d", exception_frame->error_code & 0xFFF8);

    exception_frame_info(exception_frame);
   // 判断触发异常的程序段权限级,区分系统和用户进程
    if(exception_frame->cs & 0x3)
    {
        sys_exit(exception_frame->error_code);
    }
    else{
        while(1)
        {
            hlt();
        }
    }

}
void do_handler_page_fault(exception_frame_t * exception_frame)
{   // irq_idx 14  
    log_printf("--------------------------------");
    log_printf("IRQ/Exception happend: Page fault.");
    if (exception_frame->error_code & ERROR_PAGE_P) {
        log_printf("\tpage-level protection violation: %x.", read_cr2());
    } else {
         log_printf("\tPage doesn't present %x", read_cr2());
   }
    
    if (exception_frame->error_code & ERROR_PAGE_RW) {
        log_printf("\tThe access causing the fault was a write.");
    } else {
        log_printf("\tThe access causing the fault was a read.");
    }
    
    if (exception_frame->error_code & ERROR_PAGE_US) {
        log_printf("\tA user-mode access caused the fault.");
    } else {
        log_printf("\tA supervisor-mode access caused the fault.");
    }
    
    exception_frame_info(exception_frame);

    // 判断触发异常的程序段权限级,区分系统和用户进程
    if(exception_frame->cs & 0x3)
    {
        sys_exit(exception_frame->error_code);
    }
    else{
        while(1)
        {
            hlt();
        }
    }

}

void do_handler_fpu_error(exception_frame_t * exception_frame)
{   // irq_idx 16
    do_default_handler(exception_frame,"fpu_error exception");
}
void do_handler_alignment_check(exception_frame_t * exception_frame)
{   // irq_idx 17
    do_default_handler(exception_frame,"alignment_check exception");
}
void do_handler_machine_check(exception_frame_t * exception_frame)
{   // irq_idx 18
    do_default_handler(exception_frame,"machine_check exception");
}
void do_handler_simd_exception(exception_frame_t * exception_frame)
{   // irq_idx 19
    do_default_handler(exception_frame,"simd_exception exception");
}
void do_handler_virtual_exception(exception_frame_t * exception_frame)
{   // irq_idx 20
    do_default_handler(exception_frame,"virtual_exception exception");
}
void do_handler_control_exception(exception_frame_t * exception_frame)
{   // irq_idx 21
    do_default_handler(exception_frame,"control exception");
}

// 向idt表中安装异常处理程序
// irq_idx 异常号   handler_addr 处理程序地址
int irq_install(int irq_idx, uint32_t handler_addr)
{
    if(irq_idx >= IDT_TABLE_SIZE){
        return -1;
    }
    gate_desc_set(idt_table + irq_idx, KERNEL_SELECTOR_CS, handler_addr,
        GATE_P_PRESENT | GATE_DPL0 | GATE_TYPE_IDT);
    return 0;
}

// 中断控制器初始化
void pic_init(void)
{
    outb(PIC0_ICW1, PIC_ICW1_ALWAYS_1 | PIC_ICW1_ICW4);
    outb(PIC0_ICW2, IRQ_PIC_START);
    outb(PIC0_ICW3,1<<2);
    outb(PIC0_ICW4,PIC_ICW4_8086);

    outb(PIC1_ICW1, PIC_ICW1_ALWAYS_1 | PIC_ICW1_ICW4);
    outb(PIC1_ICW2, IRQ_PIC_START+8);
    outb(PIC1_ICW3,2);
    outb(PIC1_ICW4,PIC_ICW4_8086);

    // 屏蔽8259A上所有中断，1是屏蔽，0是开启
    outb(PIC0_IMR,0xff & ~(1<<2));
    outb(PIC1_IMR,0xff);
}


// 打开中断 置0
void irq_enable(int irq_idx)
{
    // 中断是哪个引脚，引脚从0x20开始
    if(irq_idx < IRQ_PIC_START)
    {
        return ;
    }
    irq_idx -= IRQ_PIC_START;
    // 小于8 则为第一块8259A芯片中的引脚 
    // 修改PIC0_IMR的第irq_idx位
    if(irq_idx < 8)
    {
        // 打开中断 置0
        uint8_t mask = inb(PIC0_IMR) & ~(1<<irq_idx);
        outb(PIC0_IMR,mask);
    }
    //8-15 则为第二块8259A芯片中的引脚 
    // 修改PIC1_IMR的第irq_idx-8位
    else if(irq_idx < 16)
    {
        // 打开中断 置0
        irq_idx -= 8;
        uint8_t mask = inb(PIC1_IMR) & ~(1<<irq_idx);
        outb(PIC1_IMR,mask);
    }
}

// 关闭中断 置1
void irq_disable(int irq_idx)
{
    // 中断是哪个引脚，引脚从0x20开始
    if(irq_idx < IRQ_PIC_START)
    {
        return ;
    }
    irq_idx -= IRQ_PIC_START;
    // 小于8 则为第一块8259A芯片中的引脚 
    // 修改PIC0_IMR的第irq_idx位
    if(irq_idx < 8)
    {
        // 关闭中断 置1
        uint8_t mask = inb(PIC0_IMR) | (1<<irq_idx);
        outb(PIC0_IMR,mask);
    }
    //8-15 则为第二块8259A芯片中的引脚 
    // 修改PIC1_IMR的第irq_idx-8位
    else if(irq_idx < 16)
    {
        // 关闭中断 置1 
        irq_idx -= 8;
        uint8_t mask = inb(PIC1_IMR) | (1<<irq_idx);
        outb(PIC1_IMR,mask);
    }
}

void irq_disable_global(void)
{
    cli();
} 
void irq_enable_global(void)
{
    sti();
} 


void irq_send_eoi(int irq_idx)
{
    irq_idx -= IRQ_PIC_START;
    if(irq_idx >= 8)
    {
        outb(PIC1_OCW2, PIC_OCW2_EOI);
    }
    outb(PIC0_OCW2, PIC_OCW2_EOI);
}

// 进入临界区,关中断，返回之前中断开关情况
irq_status_t irq_enter_protection(void)
{   
    irq_status_t irq_status = read_eflags();
    irq_disable_global();
    return irq_status;
}

// 退出临界区，将中断设置为进入临界区之前的情况
void irq_quit_protection(irq_status_t irq_status)
{
    write_eflags(irq_status); 
}